// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StrategyModel.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge
{
    #region Using Directives

    using System;
    using System.Collections.Generic;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.Events;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Events;
    using Org.OpenTrader.Framework.Forge.Interfaces;
    using Org.OpenTrader.Framework.LiveObjects;
    using Org.OpenTrader.Framework.LiveObjects.Interfaces;

    #endregion

    /// <summary>
    /// The strategy model.
    /// </summary>
    [ConfigurationField("BookingPath", typeof(ConfigurationField.String), "The path to book deals into (front to back)", IsMandatory = true, 
        DefaultValue = @"\Strategy")]
    [ConfigurationField("TradingScope", typeof(ConfigurationField.Enum), typeof(ETradingScope), "The Trading Scope", IsMandatory = true, 
        DefaultValue = ETradingScope.Virtual)]
    [ConfigurationField("TimeFrame", typeof(ConfigurationField.Enum), typeof(EStrategyTimeFrameType), "The Strategy TimeFrame", IsMandatory = true, 
        DefaultValue = EStrategyTimeFrameType.LowFrequency)]
    [ConfigurationField("StrategyCells", typeof(ConfigurationField.Table), "The Strategy cells config", IsMandatory = true)]
    public abstract class StrategyModel : StrategyPlugin, IStrategyModel
    {
        #region Constants and Fields

        /// <summary>
        /// The StrategyModelCell collection.
        /// </summary>
        protected readonly IList<IStrategyModelCell> ListCells = new List<IStrategyModelCell>();

        /// <summary>
        /// The StrategyModelCell collection.
        /// </summary>
        protected readonly IDictionary<string, IStrategyModelCell> DicoCells = new Dictionary<string, IStrategyModelCell>();
        
        /// <summary>
        /// The identity.
        /// </summary>
        private static readonly Identity Identity = Identity.Create(System.Reflection.MethodBase.GetCurrentMethod());

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="StrategyModel"/> class. 
        /// </summary>
        /// <param name="descriptor">
        /// The descriptor.
        /// </param>
        /// <param name="container">
        /// The container.
        /// </param>
        public StrategyModel(ILiveObjectDescriptor descriptor, ILiveObjectContainer container)
            : base(descriptor, container)
        {
            Logging.Log(Identity, ELogLevel.Debug, () => string.Format("StrategyModel : creating new StrategyModel {0}", this.Name));
        }

        #endregion

        #region Properties

        /// <summary>
        /// Strategy Booking Path
        /// </summary>
        public string BookingPath { get; private set; }

        /// <summary>
        /// Strategy Expo
        /// </summary>
        public StrategyModelExpo Expo { get; private set; }

        /// <summary>
        /// Strategy Time Frame type : Low, Medium or High Frequency
        /// </summary>
        public EStrategyTimeFrameType TimeFrame { get; private set; }

        /// <summary>
        /// Strategy Trading scope
        /// </summary>
        public ETradingScope TradingScope { get; private set; }

        /// <summary>
        /// Cells Config Table
        /// </summary>
        public Table CellsConfig { get; private set; }

        /// <summary>
        /// Number of the Strategy Parameters
        /// </summary>
        public abstract int NbParams { get; }

        #endregion

        #region Indexers

        /// <summary>
        /// Get StrategyModelCell using index
        /// </summary>
        /// <param name="index">index of a StrategyModelCell</param>
        /// <returns></returns>
        public IStrategyModelCell this[int index]
        {
            get
            {
                return this.ListCells[index];
            }
        }

        /// <summary>
        /// Get StrategyModelCell using Name
        /// </summary>
        /// <param name="name">name of a StrategyModelCell</param>
        /// <returns></returns>
        public IStrategyModelCell this[string name]
        {
            get
            {
                if (this.DicoCells.ContainsKey(name))
                {
                    return this.DicoCells[name];
                }

                return null;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The on activation context.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="activationEventArgs">
        /// The activation context.
        /// </param>
        public override void OnActivation(object sender, ActivationEventArgs activationEventArgs)
        {
            switch (activationEventArgs.Type)
            {
                case EActivationType.Activation:

                    // Strategy Expo;
                    this.Expo = new StrategyModelExpo();

                    // BookingPath
                    this.BookingPath = this.Configuration["BookingPath"].ToString();

                    // Time Frame
                    this.TimeFrame = ConfigurationField.Enum.As<EStrategyTimeFrameType>(this.Configuration["TimeFrame"]);

                    // TradingScope
                    this.TradingScope = ConfigurationField.Enum.As<ETradingScope>(this.Configuration["TradingScope"]);

                    // StrategyCells
                    this.CellsConfig = this.Configuration["StrategyCells"];

                    break;

                case EActivationType.Desactivation:
                    break;
            }
        }

        #endregion

        #region Implemented Interfaces

        #region IStrategyModel

        /// <summary>
        /// Add the secotor s to the strategy
        /// </summary>
        /// <param name="s">
        /// The StrategyModelCell s : not null
        /// </param>
        /// <returns>
        /// Return true if can add the StrategyModelCell s
        /// </returns>
        public virtual bool Add(IStrategyModelCell s)
        {
            // Check if the StrategyModelCell is null
            if (s == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Add : Cannot add StrategyModelCell in {0}, Invalid StrategyModelCell", this.Name));
                return false;
            }

            // Check if the StrategyModelCell is already in the list
            var tmp = this[s.Name];
            if (tmp != null)
            {
                Logging.Log(
                    Identity, ELogLevel.Error, () => string.Format("Add : Cannot add StrategyModelCell {0}, already created in {1}", tmp.Name, this.Name));
                return false;
            }

            // Notify the strategy expo
            this.Expo.Add(s);

            // Add StrategyModelCell to the StrategyModelCell collection
            Logging.Log(Identity, ELogLevel.Debug, () => string.Format("Add : adding StrategyModelCell {0} @ {1}", s.Name, this.Name));
            this.ListCells.Add(s);
            this.DicoCells.Add(s.Name, s);
            
            return true;
        }

        /// <summary>
        /// Remove the strategy cell
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public virtual bool Remove(IStrategyModelCell s)
        {
            // Check if the StrategyModelCell is null
            if (s == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Remove : Cannot Remove StrategyModelCell in {0}, Invalid StrategyModelCell", this.Name));
                return false;
            }

            // Check if the StrategyModelCell is not in the list
            var tmp = this[s.Name];
            if (tmp == null)
            {
                Logging.Log(
                    Identity, ELogLevel.Error, () => string.Format("Remove : Cannot Remove StrategyModelCell {0}, unknown cell {1}", s.Name, this.Name));
                return false;
            }

            // Notify the strategy expo
            this.Expo.Remove(s);

            // Remove StrategyModelCell from the list
            Logging.Log(Identity, ELogLevel.Debug, () => string.Format("Remove : Removing StrategyModelCell {0} @ {1}", s.Name, this.Name));
            this.ListCells.Remove(s);
            this.DicoCells.Remove(s.Name);
            
            return true;
        }

        /// <summary>
        /// Get the number of StrategyModelCell where status
        /// </summary>
        /// <param name="status">
        /// Started, Starting, Stopped, Stopping
        /// </param>
        /// <returns>
        /// The nb of StrategyModelCell using status
        /// </returns>
        public int Nb(EStrategyStatus status)
        {
            if (status == 0)
            {
                return this.ListCells.Count;
            }

            var nb = 0;
            foreach (var cell in this.ListCells)
            {
                if (cell != null)
                {
                    if (cell.Status == status)
                    {
                        nb++;
                    }
                }
            }

            return nb;
        }

        /// <summary>
        /// OnFeed event
        /// </summary>
        /// <param name="sender">
        /// </param>
        /// <param name="args">
        /// </param>
        public abstract void OnFeed(object sender, FeedEventArgs args);

        /// <summary>
        /// OnOrder reply
        /// </summary>
        /// <param name="sender">
        /// </param>
        /// <param name="args">
        /// </param>
        public abstract void OnOrder(object sender, OrderTrackingEventArgs args);

        /// <summary>
        /// Synchronous Run (Background Worker)
        /// </summary>
        public abstract void Run();

        /// <summary>
        /// Set Parameters
        /// </summary>
        /// <param name="key">
        /// name of the parameter : not empty
        /// </param>
        /// <param name="val">
        /// the value of the parameter : not empty
        /// </param>
        /// <returns>
        /// The set param.
        /// </returns>
        public abstract bool SetParam(string key, Table.Cell val);

        /// <summary>
        /// Start StrategyModelCell at index
        /// </summary>
        /// <param name="index">
        /// index of the StrategyModelCell
        /// </param>
        /// <returns>
        /// The start.
        /// </returns>
        public virtual bool Start(int index)
        {
            var cell = this[index];
            if (cell != null)
            {
                return cell.Start();
            }

            Logging.Log(Identity, ELogLevel.Error, () => string.Format("Start : cannot start cell with index {0} @ {1}, invalid cell", index, this.Name));
            return false;
        }

        /// <summary>
        /// Start StrategyModelCell where name
        /// </summary>
        /// <param name="name">
        /// name of the StrategyModelCell
        /// </param>
        /// <returns>
        /// Return true if can start
        /// </returns>
        public virtual bool Start(string name)
        {
            var cell = this[name];
            if (cell != null)
            {
                return cell.Start();
            }

            Logging.Log(Identity, ELogLevel.Error, () => string.Format("Start : cannot start cell named {0} @ {1}, invalid cell", name, this.Name));
            return false;
        }

        /// <summary>
        /// Start All StrategyModelCell
        /// </summary>
        /// <returns>
        /// The the number of started StrategyModelCell
        /// </returns>
        public virtual int Start()
        {
            var nb = 0;
            foreach (var cell in this.ListCells)
            {
                if (cell != null)
                {
                    nb += Convert.ToInt32(cell.Start());
                }
            }

            return nb;
        }

        /// <summary>
        /// Stop StrategyModelCell at index
        /// </summary>
        /// <param name="index">
        /// index of the StrategyModelCell
        /// </param>
        /// <returns>
        /// Return true if can stop
        /// </returns>
        public virtual bool Stop(int index)
        {
            var cell = this[index];
            if (cell != null)
            {
                return cell.Stop();
            }

            Logging.Log(Identity, ELogLevel.Error, () => string.Format("Stop : cannot stop cell with index {0} @ {1}, invalid cell", index, this.Name));
            return false;
        }

        /// <summary>
        /// Stop StrategyModelCell where name
        /// </summary>
        /// <param name="name">
        /// name of the StrategyModelCell
        /// </param>
        /// <returns>
        /// Return true if can stop
        /// </returns>
        public virtual bool Stop(string name)
        {
            var cell = this[name];
            if (cell != null)
            {
                return cell.Stop();
            }

            Logging.Log(Identity, ELogLevel.Error, () => string.Format("Stop : cannot stop cell named {0} @ {1}, invalid cell", name, this.Name));
            return false;
        }

        /// <summary>
        /// Stop All StrategyModelCell
        /// </summary>
        /// <returns>
        /// Return the number of the stopped StrategyModelCell
        /// </returns>
        public virtual int Stop()
        {
            var nb = 0;
            foreach (var cell in this.ListCells)
            {
                if (cell != null)
                {
                    nb += Convert.ToInt32(cell.Stop());
                }
            }

            return nb;
        }

        #endregion

        /// <summary>
        /// Set the strategy cells
        /// </summary>
        /// <returns></returns>
        protected abstract bool SetCells();

        #endregion
    }
}